local abs = math.abs

local function node_dps_dmg(self)
   local pos = self.object:get_pos()
   local box = self.object:get_properties().collisionbox
   local pos1 = {x = pos.x + box[1], y = pos.y + box[2], z = pos.z + box[3]}
   local pos2 = {x = pos.x + box[4], y = pos.y + box[5], z = pos.z + box[6]}
   local nodes_overlap = mobkit.get_nodes_in_area(pos1, pos2)
   local total_damage = 0

   for node_def, _ in pairs(nodes_overlap) do
      local dps = node_def.damage_per_second
      if dps then
         total_damage = math.max(total_damage, dps)
      end
   end

   if total_damage ~= 0 then
      mobkit.hurt(self, total_damage)
   end
end

function unicorn.node_name_in(self, where)
   local pos = self.object:get_pos()
   local yaw = self.object:get_yaw()
   if yaw then
      local dir_x = -math.sin(yaw)
      local dir_z = math.cos(yaw)
      local pos2
      if where == 'front' then
         pos2 = {
         x = pos.x + dir_x,
         y = pos.y,
         z = pos.z + dir_z,
         }
      elseif where == 'top' then
         pos2= {
         x = pos.x,
         y = pos.y + 0.5,
         z = pos.z,
         }
      elseif where == 'below' then
         pos2 = mobkit.get_stand_pos(self)
         pos2.y = pos2.y - 0.1
      elseif where == 'back' then
         pos2 = {
         x = pos.x - dir_x,
         y = pos.y,
         z = pos.z - dir_z,
         }
      elseif where == 'self' then
         pos2= {
         x = pos.x,
         y = pos.y - 0.75,
         z = pos.z,
         }
      elseif where == 'front_top' then
         pos2= {
         x = pos.x + dir_x,
         y = pos.y + 1,
         z = pos.z + dir_z,
         }
      end
   local node = minetest.get_node_or_nil(pos2)
   if node and minetest.registered_nodes[node.name] then
      return node.name, pos2
   else
      return nil
   end
   else
      return nil
   end
end

function unicorn.check_height(self)
   local yaw = self.object:get_yaw()
   local dir_x = -math.sin(yaw) * (self.collisionbox[4] + 0.5)
   local dir_z = math.cos(yaw) * (self.collisionbox[4] + 0.5)
   local pos = self.object:get_pos()
   local ypos = pos.y - self.collisionbox[2] -- just floor level
   local pos1 = {x = pos.x + dir_x, y = ypos, z = pos.z + dir_z}
   local pos2 = {x = pos.x + dir_x, y = ypos - self.max_height, z = pos.z + dir_z}
   local blocking_node, blocking_node_pos = minetest.line_of_sight(pos1, pos2, 1)
   if not(blocking_node) then
      local height = ypos - blocking_node_pos.y
      return height
   end
   return false
end

function unicorn.check_front_obstacle(self)
   local yaw = self.object:get_yaw()
   local dir_x = -math.sin(yaw) * (self.collisionbox[4] + 0.5)
   local dir_z = math.cos(yaw) * (self.collisionbox[4] + 0.5)
   local pos = self.object:get_pos()
   local nodes_front = 5
   if minetest.line_of_sight(
      {x = pos.x + dir_x, y = pos.y, z = pos.z + dir_z}, {x = pos.x + dir_x + nodes_front, y = pos.y, z = pos.z + dir_z + nodes_front}, 1) then
      return false
   end
   return true
end

function unicorn.lq_turn2yaw(self, yaw)
   local func = function(self)
   if mobkit.turn2yaw(self, yaw) then
      return true
      end
   end
   mobkit.queue_low(self,func)
end

function unicorn.set_velocity(self, velocity)
   local yaw = self.object:get_yaw() or 0
   self.object:set_velocity({
   x = (math.sin(yaw) * -velocity.x),
   y = velocity.y or 0,
   z = (math.cos(yaw) * velocity.z),
   })
end

function unicorn.goto_next_waypoint(self,tpos,speed_fac,anim)
   speed_fac = speed_fac or 1
   anim = anim or 'walk'
   local height, pos2 = mobkit.get_next_waypoint(self,tpos)
   if not height then return false end
   if height <= 0.51 then
      local yaw = self.object:get_yaw()
      local tyaw = minetest.dir_to_yaw(vector.direction(self.object:get_pos(),pos2))
      if abs(tyaw-yaw) > 1 then
         mobkit.lq_turn2pos(self,pos2)
      end
      unicorn.lq_dumbwalk(self,pos2,speed_fac,anim)
   else
      mobkit.clear_queue_high(self)
      mobkit.lq_turn2pos(self,pos2)
      unicorn.hq_fly(self,0)
   end
   return true
end

function unicorn.lq_dumbwalk(self,dest,speed_factor, anim)
   local timer = 3         -- failsafe
   speed_factor = speed_factor or 1
   local func=function(self)
      mobkit.animate(self,anim)
      timer = timer - self.dtime
      if timer < 0 then return true end

      local pos = mobkit.get_stand_pos(self)
      local y = self.object:get_velocity().y

      if mobkit.is_there_yet2d(pos,minetest.yaw_to_dir(self.object:get_yaw()),dest) then
         if not self.isonground or abs(dest.y-pos.y) > 0.1 then
            self.object:set_velocity({x=0,y=y,z=0})
         end
         return true
      end

      if self.isonground then
         local dir = vector.normalize(vector.direction({x=pos.x,y=0,z=pos.z},{x=dest.x,y=0,z=dest.z}))
         dir = vector.multiply(dir,self.max_speed*speed_factor)
         mobkit.turn2yaw(self,minetest.dir_to_yaw(dir))
         dir.y = y
         self.object:set_velocity(dir)
      end
   end
   mobkit.queue_low(self,func)
end

function unicorn.hq_fly(self, prty)
   local func = function(self)
      if mobkit.is_queue_empty_low(self) then
         unicorn.lq_fly(self, 1.1)
      end
   end
   mobkit.queue_high(self, func, prty)
end

function unicorn.lq_fly(self)
   local func = function(self)
      mobkit.animate(self, 'fly')
      unicorn.lq_dumbfly(self, 1.1)
      self.object:set_acceleration({x = 0, y = 4, z = 0})
   end
   mobkit.queue_low(self, func)
end

function unicorn.lq_dumbfly(self, speed_factor)
   local timer = 60
   local fly_status = 'ascend'
   speed_factor = speed_factor or 1
   local func = function(self)
      timer = timer - self.dtime
      if timer < 0 then
         local velocity
         mobkit.animate(self, 'fly')
         local random_number = math.random(1,6)
         local yaw = self.object:get_yaw()
         local rotation = self.object:get_rotation()
         if random_number <= 1 or unicorn.node_name_in(self, 'front') ~= 'air' then
            if yaw then
               local rotation_integer = math.random(0, 4)
               local rotation_decimals = math.random()
               local new_yaw = yaw + rotation_integer + rotation_decimals
               unicorn.lq_turn2yaw(self, new_yaw)
               return true
            end
         end
         local y_impulse = 1
         if unicorn.check_front_obstacle(self) and unicorn.node_name_in(self, 'top') == 'air' then
            fly_status = 'ascend'
            y_impulse = 3
         end
         local altitude = unicorn.check_height(self)
         if not(altitude) or unicorn.node_name_in(self, 'top') == 'air' then
            random_number = math.random(1,10)
            if random_number < 7 then
               fly_status = 'descend'
            else
               fly_status = 'hover'
            end
         else
            local node_name = unicorn.node_name_in(self, 'below')
            if minetest.get_item_group(node_name, 'water') >= 1 then
               fly_status = 'ascend'
            end
            if altitude and (altitude < 1) then
               fly_status = 'ascend'
            end
         end
         if fly_status == 'hover' then
            velocity = {x = self.max_speed * speed_factor,
                        y = 0,
                        z = self.max_speed * speed_factor}
            self.object:set_rotation({x = 0, y = rotation.y, z = rotation.z})
            random_number = math.random(1,10)
            if random_number < 2 and not (altitude) then
               fly_status = 'descend'
            elseif random_number < 4 then
               fly_status = 'ascend'
            end
         elseif fly_status == 'descend' then
            velocity = {x = self.max_speed * speed_factor,
                        y = -speed_factor,
                        z = self.max_speed * speed_factor}
            self.object:set_rotation({x = .16, y = rotation.y, z = rotation.z})
            random_number = math.random(1,10)
            if random_number < 2 then
               fly_status = 'hover'
            elseif random_number < 4 then
               fly_status = 'ascend'
            end
         else
            fly_status = 'ascend'
            velocity = {x = self.max_speed * speed_factor,
                        y = speed_factor * (y_impulse or 1),
                        z = self.max_speed * speed_factor}
            self.object:set_rotation({x = -0.16, y = rotation.y, z = rotation.z})
         end
         timer = 60
         unicorn.set_velocity(self, velocity)
         self.fly_velocity = velocity
         return true
      else
         if self.fly_velocity then
            unicorn.set_velocity(self, self.fly_velocity)
         else
            unicorn.set_velocity(self, {x = 0.0, y = 0.0, z = 0.0})
         end
      end
   end
   mobkit.queue_low(self, func)
end

function unicorn.hq_runfrom(self,prty,tgtobj)
   local init=true
   local timer=6
   local func = function(self)

      if not mobkit.is_alive(tgtobj) then return true end
      if init then
         timer = timer-self.dtime
         if timer <=0 or vector.distance(self.object:get_pos(),tgtobj:get_pos()) < 8 then
            mobkit.make_sound(self,'scared')
            init=false
         end
         return
      end

      if mobkit.is_queue_empty_low(self) and self.isonground then
         local pos = mobkit.get_stand_pos(self)
         local opos = tgtobj:get_pos()
         if vector.distance(pos,opos) < self.view_range*1.1 then
            local tpos = {x=2*pos.x - opos.x, y=opos.y, z=2*pos.z - opos.z}
            unicorn.goto_next_waypoint(self,tpos,1.1,'gallop')
         else
            self.object:set_velocity({x=0,y=0,z=0})
            return true
         end
      end
   end
   mobkit.queue_high(self,func,prty)
end

function unicorn.brain(self)
   local pos = self.object:get_pos()
   local die = false

   mobkit.vitals(self)

   if self.hp <= 0 then
      die = true
      mobkit.clear_queue_high(self)
      mobkit.hq_die(self)
      mokapi.drop_items(self)
      return
   end

   if die then
      petz.on_die(self)
      return
   end

   if mobkit.timer(self,1) then
      local prty = mobkit.get_queue_priority(self)
      local pos = self.object:get_pos()

      if prty < 25 then
         if self.driver then
            petz.hq_mountdriver(self, 25)
            return
         end
      end

      if prty < 20 and self.isinliquid then
         mobkit.hq_liquid_recovery(self,20)
         return
      end

      if prty < 16 then
         local player = mobkit.get_nearby_player(self)
         if petz.bh_start_follow(self, pos, player, 16) then
            return
         end
      end

      if prty == 16 then
         local player = mobkit.get_nearby_player(self)
         if petz.bh_stop_follow(self, player) then
            return
         end
      end

      if prty < 10 then
         if not(self.tamed) then
            local player = mobkit.get_nearby_player(self)
            if player then
               local player_pos = player:get_pos()
               local player_wield = player:get_wielded_item():get_name()
               if vector.distance(pos, player_pos) < 8 and player_wield ~= 'unicorn:apple' then
                  unicorn.hq_runfrom(self,10,player)
                  return
               end
            end
         end
      end

      if mobkit.is_queue_empty_high(self) and not(self.status) and not(self.wagon) then
         mobkit.hq_roam(self,0)
      end
   end
end
